package com.opdar.platform.gateway.point;

import com.opdar.platform.utils.ResourceUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.asm.ClassReader;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by shiju on 2017/12/13.
 */
public class PointManager<T> {
    private Map<String,Point> points = new HashMap<String, Point>();
    private Class<T> apiClz = null;
    private Set<String> pointSets = new HashSet<String>();
    private Map<String,LinkedList<String>> classCode = new HashMap<String, LinkedList<String>>();
    private static PointManager pointManager = null;
    private Logger logger = LoggerFactory.getLogger(getClass());
    private static ThreadLocal<String> currentCode = new ThreadLocal<String>();

    public PointManager(Class<T> apiClz,boolean autoScan) {
        init(apiClz,autoScan);
    }

    public PointManager() {
    }

    public static void setCurrentCode(String currentCode) {
        PointManager.currentCode.set(currentCode);
    }

    public void init(Class<T> apiClz) {
        init(apiClz,false);
    }

    public void init(Class<T> apiClz,boolean autoScan) {
        this.apiClz = apiClz;
        Method[] methods = apiClz.getDeclaredMethods();
        for(Method method:methods){
            String p = apiClz.getName()+"."+method.getName();
            pointSets.add(p);
        }

        if(autoScan){
            scan("");
        }
    }

    public Map<String, Point> getPoints() {
        return points;
    }

    public Class<?> getApiClz() {
        return apiClz;
    }

    public static PointManager getInstance(){
        if(pointManager == null){
            pointManager = new PointManager();
        }
        return pointManager;
    }

    public boolean is(String methodName){
        return pointSets.contains(methodName);
    }

    public void add(Point point){
        points.put(point.getCode(),point);
        logger.info("Add Point,Code:[{}] ",point.getCode());
        logger.info("    Class:[{}]",point.getClassName());
        logger.info("    Method:[{}] LineNumber:[{}]",point.getMethod(),point.getLineNumber());
        LinkedList<String> list = null;
        if(classCode.containsKey(point.getClassName())){
            list = classCode.get(point.getClassName());
        }else{
            classCode.put(point.getClassName(),list = new LinkedList<String>());
        }
        list.addLast(point.getCode());
    }

    public Point get(String code){
        return points.get(code);
    }

    public void scan(final String packageName){
        ResourceUtils.find(new ResourceUtils.FileFinder() {
            @Override
            public String suffix() {
                return ".class";
            }

            @Override
            public String getPackageName() {
                return packageName;
            }

            @Override
            public void call(String s, String s1, String s2) {
                String cls = s+s1;
                try {
                    ClassReader cr = new ClassReader(cls);
                    PointClassPrinter cp = new PointClassPrinter(PointManager.this);
                    cr.accept(cp ,0);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public static void clearCurrentCode() {
        PointManager.currentCode.remove();
    }

    public static String getCurrentCode() {
        return PointManager.currentCode.get();
    }
}
